"""
Created on Oct 29, 2009

@author: aamn
"""

import numpy as np


class Methods(object):
    """ Methods for data classes in the SSAP data model
    """
    _endian = 'B' # fits standard
    _ints = np.dtype(np.long).newbyteorder(_endian)
    _doubles = np.dtype(np.double).newbyteorder(_endian)
    _floats = np.dtype(np.float32).newbyteorder(_endian)
    _chars = np.dtype(np.object_)

    def __init__(self, **kwargs):
        self._kwargs(kwargs)

    def __getitem__(self, item):
        if item not in self._model:
            return None
        return self._model[item]

    def __getattr__(self, attribute):
        if attribute == 'names': return self._popzero().keys()
        else: return self._get(attribute)

    def _get(self, args):
        """ return properties of the data class as dict
        """
        if len(args) is 0:
          attribute = 'datamodel'
        elif isinstance(args, str):
          attribute = args
        elif isinstance(args, list):
          attribute = args[0]

        if attribute in self._getzero().keys():
            return self._key(attribute)
        elif attribute == 'datamodel':
            return self._popzero()
        else:
            raise AttributeError(attribute)

    def _key(self, key):
        """ map one of the data class params to the data class key
        """
        return dict([(foo, bar[key]) for foo, bar in self._popzero().items()])

    def _kwargs(self, kwargs):
        """ process kwargs for this Method class
        """
        if 'length' in kwargs:
          self._length = kwargs.pop('length')
        elif not hasattr(self, '_length'):
          self._length = 1
        if 'doubles' in kwargs:
          self._doubles = np.dtype(kwargs.pop('doubles')).newbyteorder(self._endian)
        if 'floats' in kwargs:
          self._floats = np.dtype(kwargs.pop('floats')).newbyteorder(self._endian)
        if 'ints' in kwargs:
          self._ints = np.dtype(kwargs.pop('ints')).newbyteorder(self._endian)
        if 'chars' in kwargs:
          self._chars = np.dtype(kwargs.pop('chars'))
        self._update()
        return

    def _update(self):
        """ return the data class with the dtype as modified
        """
        Unit = self._model.copy()
        dnames = [Unit[foo].update(dtype=self._doubles)
           for foo, bar in Unit.items() if bar['datatype'] == 'double']
        fnames = [Unit[foo].update(dtype=self._floats)
           for foo, bar in Unit.items() if bar['datatype'] == 'double']
        lnames = [Unit[foo].update(dtype=self._ints)
           for foo, bar in Unit.items() if bar['datatype'] == 'long']
        cnames = [Unit[foo].update(dtype=self._chars)
           for foo, bar in Unit.items() if bar['datatype'] == 'char']
        znames = [Unit[foo].update(dtype='the modified dtype for this element')
           for foo, bar in Unit.items() if foo == 0]
        self._model = Unit
        return

    def _getzero(self):
        """ return the 0 element description
        """
        Unit = self._model.copy()
        return Unit.pop(0)

    def _popzero(self):
        """ Pop the 0 element description
        """
        Unit = self._model.copy()
        Unit.pop(0)
        return Unit


class Protocol(object):
    """ base object class for building protocols
    """
    def __init__(self, **kwargs):
        if len(kwargs) > 0:
            for n, v in kwargs.items():
                object.__setattr__(self, n, v)

    def __setattr__(self, name, value):
        return None

    def __getattr__(self, attr):
        return None

class SSAP(Protocol):
    """ the Simple Spectral Acesss Protocol Wrapper
    """
    def __init__(self):
        version = '1.04'
        title = "Simple Spectral Axis Protocol"
        protocol = "SSA"
        Protocol.__init__(self, version=version, protocol=protocol, title=title)

    def __setattr__(self, name, value):
        if name not in self.__dict__:
            object.__setattr__(self, name, value)

class DataID(SSAP):
    pass

class Curation(SSAP):
    pass

class Target(SSAP):
    pass

class Derived(SSAP):
    pass

class CoordSys(SSAP):
    pass

class Char(SSAP):
    pass

class Data(SSAP):
    """
    General Spec Container Class
    """
    class SpectralAxis(Methods):
          """
          The SpectralAxis element Accuracy is flattened, i.e., from 
          Accuracy.BinSize -> BinSize. See property "utype"
            
          Usage:
            
            dtype = SpectralAxis.dtype
            property = SpectralAxis.property
              e.g., SpectralAxis.UCD
                  
              keyword args (optional):
              
                floats=np.double  : data bit pixel.
                chars= np.object_
                ints=np.long
          """
          # Data.Spectral Axis
          # these are arrays
          # Data.SpectralAxis.Value   Spectral coordinates for points double
          # Data.SpectralAxis.Accuracy.BinSize  em;spect.binSize  Spectral coord bin size double
          # Data.SpectralAxis.Accuracy.BinLow  em;stat.min  Spectral coord bin lower end double
          # Data.SpectralAxis.Accuracy.BinHigh  em;stat.max  Spectral coord bin upper end double
          # Data.SpectralAxis.Accuracy.StatError  em;stat.error  Spectral coord measurement error double
          # Data.SpectralAxis.Accuracy.StatErrLow  em;stat.error;stat.min  Spectral coord measurement lower error double
          # Data.SpectralAxis.Accuracy.StatErrHigh  em;stat.error;stat.max  Spectral coord measurement upper error double
          # Data.SpectralAxis.Accuracy.SysError  em;stat.error  Spectral coord systematic error double
          # Data.SpectralAxis.Resolution  spect.resolution  Spectral resolution FWHM double
          # these are not. they can map to each array as a named attribute.
          # Data.SpectralAxis.Ucd   UCD for spectral coord char *
          # Data.SpectralAxis.Unit   Unit for spectral coord char *
          _model = {
                    (0):{'datatype':'is the direct datatype from the SSAP model',
                        'UCD':'is the direct UCS in the SSAP model',
                        'description':'is the description from the SSAP model',
                        'utype':'is the actual utype from the SSAP model'},
                    'Value':{'datatype':'double',
                        'UCD':'',
                        'description':'Spectral coordinates for points',
                        'utype':'Data.SpectralAxis.Value'},
                    'BinSize':{'datatype':'double',
                        'UCD':'em;spect.binSize',
                        'description':'Spectral coord bin size',
                        'utype':'Data.SpectralAxis.Accuracy.BinSize'},
                    'BinLow':{'datatype':'double',
                        'UCD':'em;stat.min',
                        'description':'Spectral coord bin lower end',
                        'utype':'Data.SpectralAxis.Accuracy.BinLow'},
                    'BinHigh':{'datatype':'double',
                        'UCD':'em;stat.max',
                        'description':'Spectral coord bin upper end',
                        'utype':'Data.SpectralAxis.Accuracy.BinHigh'},
                    'StatError':{'datatype':'double',
                        'UCD':'em;stat.error',
                        'description':'Spectral coord measurement error',
                        'utype':'Data.SpectralAxis.Accuracy.StatError'},
                    'StatLow':{'datatype':'double',
                        'UCD':'em;stat.min',
                        'description':'Spectral coord lower error',
                        'utype':'Data.SpectralAxis.Accuracy.StatError'},
                    'StatHigh':{'datatype':'double',
                        'UCD':'em;stat.max',
                        'description':'Spectral coord upper error',
                        'utype':'Data.SpectralAxis.Accuracy.StatHigh'},
                    'SysError':{'datatype':'double',
                        'UCD':'em;stat.error',
                        'description':'Spectral coord systematic error',
                        'utype':'Data.SpectralAxis.Accuracy.SysError'},
                    'Resolution':{'datatype':'double',
                        'UCD':'spect.resolution',
                        'description':'Spectral resolution FWHM',
                        'utype':'Data.SpectralAxis.Resolution'},
                    'Ucd':{'datatype':'char',
                        'UCD':'',
                        'description':'UCD for spectral coord',
                        'utype':'Data.SpectralAxis.Ucd'},
                    'Unit':{'datatype':'char',
                        'UCD':'',
                        'description':'Unit for spectral coord',
                        'utype':'Data.SpectralAxis.Unit'}
                  }

    class FluxAxis(Methods):
          """
          The FluxAxis element Accuracy is flattened, i.e., from 
          Accuracy.BinSize -> BinSize. See property "utype"
            
          Usage:
            
            dtype = FluxAxis.dtype
            property = FluxAxis.property
              e.g., FluxAxis.UCD
                  
              keyword args (optional):
              
                floats=np.double  : data bit pixel.
                chars= np.object_
                ints=np.long
          """
          # Data.Spectral Axis
          # these are arrays
          # Data.FluxAxis
          # Data.FluxAxis.Value   Flux values for points double
          # Data.FluxAxis.Accuracy.StatError  phot.flux.density;em;stat.error  Flux symmetric statistical error double
          # Data.FluxAxis.Accuracy.StatErrLow  phot.flux.density;em;stat.error;stat.min  Flux lower error double
          # Data.FluxAxis.Accuracy.StatErrHigh  phot.flux.density;em;stat.error;stat.max  Flux upper error double
          # Data.FluxAxis.Accuracy.SysError  phot.flux.density;em;stat.error.sys  Flux systematic error double
          # This is a mask array
          # Data.FluxAxis.Quality  meta.code.qual;phot.flux.density;em  Flux measurement quality mask long
          #
          # I don't know to do with this one.
          # Data.FluxAxis.Quality.n
          # 
          # Data.FluxAxis.Ucd   UCD for flux char *
          # Data.FluxAxis.Unit   Unit for flux char *
          _model = {
                    (0):{'datatype':'one of [double,long,char]',
                        'UCD':'Unified Content Descripter',
                        'description':'short text',
                        'utype':'IVOA utypes'},
                    'Value':{'datatype':'double',
                        'UCD':'',
                        'description':'Flux values for points',
                        'utype':'Data.FluxAxis.Value'},
                    'StatError':{'datatype':'double',
                        'UCD':'phot.flux.density;em;stat.error',
                        'description':'Flux measurement error',
                        'utype':'Data.FluxAxis.Accuracy.StatError'},
                    'StatLow':{'datatype':'double',
                        'UCD':'phot.flux.density;em;stat.min',
                        'description':'Flux lower error',
                        'utype':'Data.FluxAxis.Accuracy.StatError'},
                    'StatHigh':{'datatype':'double',
                        'UCD':'phot.flux.density;em;stat.max',
                        'description':'Flux upper error',
                        'utype':'Data.FluxAxis.Accuracy.StatHigh'},
                    'SysError':{'datatype':'double',
                        'UCD':'phot.flux.density;em;stat.error',
                        'description':'Flux systematic error',
                        'utype':'Data.FluxAxis.Accuracy.SysError'},
                    'Quality':{'datatype':'long',
                        'UCD':'meta.code.qual;phot.flux.density;em',
                        'description':'Flux measurement quality mask',
                        'utype':'Data.FluxAxis.Resolution'},
                    'Ucd':{'datatype':'char',
                        'UCD':'',
                        'description':'UCD for flux',
                        'utype':'Data.FluxAxis.Ucd'},
                    'Unit':{'datatype':'char',
                        'UCD':'',
                        'description':'Unit for flux',
                        'utype':'Data.FluxAxis.Unit'},
                    'DefQuality':{'datatype':'char',
                        'UCD':'',
                        'description':' Meaning of quality value',
                        'utype':'Data.FluxAxis.Quality.n'}
                  }

    class TimeAxis(Methods):
        pass

    class BackgroundModel(Methods):
        pass
